#include "pixelMap.h"
#include <GL/glui.h>
#include <iostream>
#include <cmath>

#define INC_OP 0
#define DEC_OP 1
#define AVG_OP 2

///Acoes GLUI
#define FILTER         0x01
#define ARITHMETIC     0x02
#define LOGIC          0x03
#define GEOMETRIC      0x04
#define VIEW_ROTATE    0x05
#define VIEW_SCALE     0x06
#define VIEW_TRANSLATE 0x07

using namespace std;

void idleFunc( void );
void pictureDisplay( void );
void pictureInit( void );
void keyboard( unsigned char key, int x, int y );
void mouse( int button, int state, int x, int y );

///Funcoes GLUI
void controlCallback (int control);

void setPictures( char* img1, char* img2 );

void pixelAnd( void );
void pixelOr( void );
void pixelXor( void );
bool convertToBin( int i, int j, pixel** pixels );

void scale(pixel** matrixResult, pixel** matrix, int h, int w, float sx, float sy);
void translate(pixel** matrixResult, pixel** matrix, int h, int w, int tx, int ty);
void rotate(pixel** matrixResult, pixel** matrix, int h, int w, float theta);

int winMain;
int h1, w1, h2, w2;
bool isGrayScale = false;

PixelMap* pixelMapResult;
pixel** pixels1;
pixel** pixels2;
pixel** resultPixels;
uByte *data;

///Variaveis do GLUI
char image1[30] = "lena.bmp";
char image2[30] = "lena.bmp";

GLUI* glui;
GLUI_Panel* panelMain;

GLUI_Panel* panelFilter;
GLUI_RadioGroup* radioGroupFilter;

GLUI_Panel* panelImages;
GLUI_EditText *editImage1;
GLUI_EditText *editImage2;

GLUI_Panel* panelArithmetic;
GLUI_RadioGroup* radioGroupArithmetic;
GLUI_Button* buttonArithmetic;

GLUI_Panel* panelLogic;
GLUI_RadioGroup* radioGroupLogic;
GLUI_Button* buttonLogic;

GLUI_Panel* panelGeometric;
GLUI_RadioGroup* radioGroupGeometric;

GLUI_Panel* panelRotate;
GLUI_EditText* editRotate;
GLUI_Button* buttonRotate;

GLUI_Panel* panelScale;
GLUI_EditText* editScaleX;
GLUI_EditText* editScaleY;
GLUI_Button* buttonScale;

GLUI_Panel* panelTranslate;
GLUI_EditText* editTranslateX;
GLUI_EditText* editTranslateY;
GLUI_Button* buttonTranslate;

int main(int argc, char** argv)
{
    setPictures( image1, image2 );

    glutInit( &argc, argv );

    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
    glutInitWindowSize( w1, h1 );
    glutInitWindowPosition( 100, 100 );
    winMain = glutCreateWindow ( "Resultado" );
    glutDisplayFunc( pictureDisplay );
    pictureInit();

    glui = GLUI_Master.create_glui( "Filter Control", 0, 1000, 100 );
    panelMain = glui->add_panel( "", GLUI_PANEL_NONE );

    panelFilter = glui->add_panel_to_panel( panelMain, "Filter" );
    radioGroupFilter = glui->add_radiogroup_to_panel( panelFilter, NULL, FILTER, controlCallback );
    glui->add_radiobutton_to_group( radioGroupFilter, "Arithmetic" );
    glui->add_radiobutton_to_group( radioGroupFilter, "Logic" );
    glui->add_radiobutton_to_group( radioGroupFilter, "Translate" );

    panelImages = glui->add_panel_to_panel( panelMain, "Images" );
    editImage1 = glui->add_edittext_to_panel( panelImages, "Image 1: " );
    editImage2 = glui->add_edittext_to_panel( panelImages, "Image 2: " );

    panelArithmetic = glui->add_panel_to_panel( panelMain, "Arithmetic" );
    radioGroupArithmetic = glui->add_radiogroup_to_panel( panelArithmetic );
    glui->add_radiobutton_to_group( radioGroupArithmetic, "Sum" );
    glui->add_radiobutton_to_group( radioGroupArithmetic, "Sub" );
    glui->add_radiobutton_to_group( radioGroupArithmetic, "Avg" );
    buttonArithmetic = glui->add_button_to_panel( panelArithmetic, "View", ARITHMETIC, controlCallback );

    panelLogic = glui->add_panel_to_panel( panelMain, "Logic" );
    radioGroupLogic = glui->add_radiogroup_to_panel( panelLogic );
    glui->add_radiobutton_to_group( radioGroupLogic, "AND" );
    glui->add_radiobutton_to_group( radioGroupLogic, "OR" );
    glui->add_radiobutton_to_group( radioGroupLogic, "XOR" );
    buttonLogic = glui->add_button_to_panel( panelLogic, "View", LOGIC, controlCallback );
    panelLogic->disable();

    panelGeometric = glui->add_panel_to_panel( panelMain, "Geometric" );
    radioGroupGeometric = glui->add_radiogroup_to_panel( panelGeometric, NULL, GEOMETRIC, controlCallback );
    panelGeometric->disable();

    glui->add_radiobutton_to_group( radioGroupGeometric, "Rotate" );
    panelRotate = glui->add_panel_to_panel( panelGeometric, "Rotate" );
    editRotate = glui->add_edittext_to_panel( panelRotate, "Angle(degrees): " );
    buttonRotate = glui->add_button_to_panel( panelRotate, "View", VIEW_ROTATE, controlCallback );

    glui->add_radiobutton_to_group( radioGroupGeometric, "Scale" );
    panelScale = glui->add_panel_to_panel( panelGeometric, "Scale" );
    editScaleX = glui->add_edittext_to_panel( panelScale, "X: " );
    editScaleY = glui->add_edittext_to_panel( panelScale, "Y: " );
    buttonScale = glui->add_button_to_panel( panelScale, "View", VIEW_SCALE, controlCallback );


    glui->add_radiobutton_to_group( radioGroupGeometric, "Translate" );
    panelTranslate = glui->add_panel_to_panel( panelGeometric, "Translate" );
    editTranslateX = glui->add_edittext_to_panel( panelTranslate, "X: " );
    editTranslateY = glui->add_edittext_to_panel( panelTranslate, "Y: " );
    buttonTranslate = glui->add_button_to_panel( panelTranslate, "View", VIEW_TRANSLATE, controlCallback );

    glui->set_main_gfx_window( winMain );
    GLUI_Master.set_glutIdleFunc( idleFunc );

    glutMainLoop();
    return 0;
}

void idleFunc( void )
{
    if ( glutGetWindow() !=  winMain) glutSetWindow(winMain);
    glutPostRedisplay();
}

void pictureDisplay( void )
{
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    pixelMapResult->ViewImage();

    glutSwapBuffers();
    glutPostRedisplay();
}


void pictureInit ( void )
{
    // selecionar cor de fundo (preto)
    glClearColor( 0.0, 0.0, 0.0, 0.0 );
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    // glOrtho( 0.0, w1, 0.0, h1, -1.0, 1.0 );

    glMatrixMode(GL_MODELVIEW);				// Select The Modelview Matrix
    glLoadIdentity();							// Inicializa com matriz identidade
}

void setPictures( char* img1, char* img2 )
{
    PixelMap* pixelMap1;
    PixelMap* pixelMap2;


    pixelMap1 = new PixelMap(img1);
    h1 = pixelMap1->GetHeight();
    w1 = pixelMap1->GetWidth();

    pixelMap2 = new PixelMap(img2);
    h2 = pixelMap2->GetHeight();
    w2 = pixelMap2->GetWidth();

    pixelMapResult = new PixelMap(img1);

    if (isGrayScale)
    {
        pixelMap1->ConvertToGrayScale();
        pixelMap2->ConvertToGrayScale();
        pixelMapResult->ConvertToGrayScale();
    }


    resultPixels = (pixel **) malloc(h1 * sizeof(pixel *));
    for (int i = 0; i < h1; i++)
    {
        resultPixels[i] = (pixel *) malloc(w1  * sizeof(pixel));
        /*for (int j = 0; j < w1; j++)
        {
            resultPixels[i][j].value = 0;
        }*/
    }

    pixelMapResult->ConvertDataToPixels(resultPixels);

    pixels1 = (pixel **) malloc(h1 * sizeof(pixel *));
    for (int i = 0; i < h1; i++)
    {
        pixels1[i] = (pixel *) malloc(w1  * sizeof(pixel));
    }
    pixelMap1->ConvertDataToPixels(pixels1);

    pixels2 = (pixel **) malloc(h2 * sizeof(pixel *));
    for (int i = 0; i < h2; i++)
    {
        pixels2[i] = (pixel *) malloc(w2  * sizeof(pixel));
    }
    pixelMap2->ConvertDataToPixels(pixels2);
}

bool convertToBin(int i, int j, pixel** pixels)
{
    if ((int)pixels[i][j].value < 230)
        return true;
    else
        return false;
}

void pixelAnd()
{
    int h, w;
    if (h1 < h2) h = h1;
    else h = h2;
    if (w1 < w2) w = w1;
    else w = w2;
    for (int i = 0; i < h; i++)
    {
        for (int j = 0; j < w; j++)
        {
            if (convertToBin(i, j, pixels1) && convertToBin(i, j, pixels2))
            {

                //  cout << convertToBin(i, j, pixels1) << "  ,  " << convertToBin(i, j, pixels2) << " | ";
                resultPixels[i][j].value = (uByte)0;
            }
            else resultPixels[i][j].value = (uByte)255;
        }
        //if(j == 50) exit(0);

    }
    pixelMapResult->ConvertPixelsToData(resultPixels);
}

void pixelOr()
{
    int h, w;
    if (h1 < h2) h = h1;
    else h = h2;
    if (w1 < w2) w = w1;
    else w = w2;
    for (int i = 0; i < h; i++)
    {
        for (int j = 0; j < w; j++)
        {
            if (convertToBin(i, j, pixels1) || convertToBin(i, j, pixels2))
            {

                //  cout << convertToBin(i, j, pixels1) << "  ,  " << convertToBin(i, j, pixels2) << " | ";
                resultPixels[i][j].value = (uByte)0;
            }
            else resultPixels[i][j].value = (uByte)255;
        }
        //if(j == 50) exit(0);

    }
    pixelMapResult->ConvertPixelsToData(resultPixels);
}

void pixelXor()
{
    int h, w;
    if (h1 < h2) h = h1;
    else h = h2;
    if (w1 < w2) w = w1;
    else w = w2;
    for (int i = 0; i < h; i++)
    {
        for (int j = 0; j < w; j++)
        {
            if (convertToBin(i, j, pixels1) && convertToBin(i, j, pixels2))
            {
                resultPixels[i][j].value = (uByte)255;
            }
            else if (convertToBin(i, j, pixels1) || convertToBin(i, j, pixels2))
                resultPixels[i][j].value = (uByte)0;
        }
    }
    pixelMapResult->ConvertPixelsToData(resultPixels);
}

void scale(pixel** matrixResult, pixel** matrix, int h, int w, float sx, float sy)
{
    for (int u = 0; u < h; u++)
    {
        for (int v = 0; v < w; v++)
        {
            matrixResult[u][v].R = 0;
            matrixResult[u][v].G = 0;
            matrixResult[u][v].B = 0;
        }
    }

    for (int i = 0; i < h; i++)
    {
        for (int j = 0; j < w; j++)
        {
            int u = (i - (h/2))*sx + (h/2);
            int v = (j - (w/2))*sy + (w/2);

            if ((u > -1) && (u < h) && (v > -1) && (v < w))
            {
                matrixResult[u][v].R = matrix[i][j].R;
                matrixResult[u][v].G = matrix[i][j].G;
                matrixResult[u][v].B = matrix[i][j].B;
            }
        }
    }
    pixelMapResult->ConvertPixelsToData(matrixResult);
}

void translate(pixel** matrixResult, pixel** matrix, int h, int w, int tx, int ty)
{
    for (int i = 0; i < h; i++)
    {
        for (int j = 0; j < w; j++)
        {
            int u = i + tx;
            int v = j + ty;

            if ((u < h) && (v < w))
            {
                matrixResult[u][v].R = matrix[i][j].R;
                matrixResult[u][v].G = matrix[i][j].G;
                matrixResult[u][v].B = matrix[i][j].B;
            }
            else
            {
                u = (u < h) ? u : u-h;
                v = (v < w) ? v : v-w;
                matrixResult[u][v].R = 0;
                matrixResult[u][v].G = 0;
                matrixResult[u][v].B = 0;
            }
        }
    }
    pixelMapResult->ConvertPixelsToData(matrixResult);
    //pixelMapResult->ViewImage();
}

void rotate(pixel** matrixResult, pixel** matrix, int h, int w, float theta)
{
    for (int u = 0; u < h; u++)
    {
        for (int v = 0; v < w; v++)
        {
            matrixResult[u][v].R = 0;
            matrixResult[u][v].G = 0;
            matrixResult[u][v].B = 0;
        }
    }

    for (int i = 0; i < h; i++)
    {
        for (int j = 0; j < w; j++)
        {
            int u = (i-(h/2))*cos(theta) - (j-(w/2))*sin(theta) + (h/2);
            int v = (i-(h/2))*sin(theta) + (j-(w/2))*cos(theta) + (w/2);

            if ((u > -1) && (u < h) && (v > -1) && (v < w))
            {
                matrixResult[u][v].R = matrix[i][j].R;
                matrixResult[u][v].G = matrix[i][j].G;
                matrixResult[u][v].B = matrix[i][j].B;
            }
        }
    }
    pixelMapResult->ConvertPixelsToData(matrixResult);
}

void controlCallback( int control )
{
    switch( control )
    {
        case FILTER:
        {
            switch( radioGroupFilter->get_int_val() )
            {
                ///Arithmetic
                case 0:
                    panelArithmetic->enable();
                    panelLogic->disable();
                    panelGeometric->disable();
                    break;

                ///Logic
                case 1:
                    panelArithmetic->disable();
                    panelLogic->enable();
                    panelGeometric->disable();
                    break;

                ///Geometric
                case 2:
                    panelArithmetic->disable();
                    panelLogic->disable();
                    panelGeometric->enable();

                    panelScale->disable();
                    panelTranslate->disable();
                    radioGroupGeometric->set_selected( 0 );
                    glui->refresh();
                    break;
            }
            break;
        }

        case ARITHMETIC:
        {
            break;
        }

        case LOGIC:
        {
            break;
        }

        case GEOMETRIC:
        {
            switch( radioGroupGeometric->get_int_val() )
            {
                case 0:
                    panelRotate->enable();
                    panelScale->disable();
                    panelTranslate->disable();
                    break;

                case 1:
                    panelRotate->disable();
                    panelScale->enable();
                    panelTranslate->disable();
                    break;

                case 2:
                    panelRotate->disable();
                    panelScale->disable();
                    panelTranslate->enable();
                    break;
            }
            break;
        }

        case VIEW_ROTATE:
        {
            break;
        }

        case VIEW_SCALE:
        {
            break;
        }

        case VIEW_TRANSLATE:
        {
            break;
        }
    }
}
